/*
 * inotify-sync - a simple file synchronizer and file system watcher
 * Copyright (C) 2010-2011, inotify-sync developers and inotify-sync contributors
 * Copyright (C) 2010-2011, Cohesion Network Security Studio
 *
 * inotify-sync is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * inotify-sync is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with inotify-sync; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

/**
 * @file	ProfileXml.cpp
 * @author	h2s
 * @date	2011-02-15
 * @remarks	Singleton Pattern
 */

#include "ProfileXml.h"

ProfileXml::ProfileXml(string profile_path)
try
:
p_doc(0),
p_root_node(0),
p_cur_node(0)
{
	this->profile_path=profile_path;
/*检测是否存在profile文件
 *如果不存在则创建
 *如果存在则查看是否具有读写权限
 *如果无读或写权限或者不能创建
 *则直接报错退出
 */
	int ret=0;
	if(0==(ret=access(profile_path.c_str(),F_OK)))
	{
		if(0==(ret=access(profile_path.c_str(),R_OK|W_OK)))
		{
			xmlKeepBlanksDefault(0);
			p_doc=xmlReadFile(profile_path.c_str(),
						"UTF-8",
						XML_PARSE_RECOVER);	
			p_root_node=xmlDocGetRootElement(p_doc);
		}
		else
		{
			throw Error(errno,E_PROFPERMDENY);
		}
	}
	else
	{
		int fd;
		mode_t mode=S_IRUSR|S_IWUSR;
		if(-1!=(fd=creat(profile_path.c_str(),mode)))
		{
			close(fd);
			xmlKeepBlanksDefault(0);
			p_doc=xmlNewDoc(BAD_CAST"1.0");
			p_root_node=xmlNewNode(0,BAD_CAST"inotify_sync_profiles");
			xmlNewProp(p_root_node,
				BAD_CAST"version",BAD_CAST"1.0");
			xmlDocSetRootElement(p_doc,p_root_node);
		}
		else
		{
			throw Error(errno,E_PROFCREATERR);
		}
	}
//如果没有profile则生成默认的sample_profile
	vector<string> svec;
	this->get_profile_list(svec);
	if(svec.size()==0)
	{
		vector<string> ignore_path_vec;
		ignore_path_vec.push_back("/home/liyiwei/test/ignored");
		Profile sample_profile=Profile("sample_profile",
			RSYNC,
			STD,
			"/home/liyiwei/inotify-sync/log/sample_profile.log",
			true,
			"/home/liyiwei/test",
			"/home/liyiwei/test2",
			ignore_path_vec,
			true,
			true,
			true,
			true,
			true,
			true,
			true);
		this->add_profile(sample_profile);
	}
}
catch(Error& error)
{
	error.print_error();
	exit(error.get_error_id());
}

ProfileXml::~ProfileXml()
{
	xmlSaveFormatFileEnc(profile_path.c_str(),
					p_doc,"UTF-8",1);
	xmlFreeDoc(p_doc);
	xmlCleanupParser();
	xmlMemoryDump();
}

bool
ProfileXml::add_profile(Profile& profile)
{
	if(has_profile(profile.get_profile_name()))
		return false;
//如果不存在profile，则新建
	xmlNodePtr p_profile_node=xmlNewNode(0,BAD_CAST"profile");
//设置profile节点属性name
	xmlSetProp(p_profile_node,BAD_CAST"name",
		BAD_CAST profile.get_profile_name().c_str());

	xmlChar *comment;
	xmlNodePtr p_comment_node;

//设置profile的同步模式
	comment=BAD_CAST
    "\n\
    sync_mode: RSYNC,ASYNC,NSYNC\n\
    RSYNC real time synchronization when event occured\n\
    ASYNC asynchronous synchronization when user asked\n\
    NSYNC no synchronize\n\
    ";
	p_comment_node=xmlNewComment(comment);
	xmlNodePtr p_sync_mode_node=xmlNewNode(0,BAD_CAST"sync_mode");
	xmlNodeSetContent(p_sync_mode_node,
		BAD_CAST profile.get_sync_mode_str().c_str());
	xmlAddChild(p_profile_node,p_comment_node);
	xmlAddChild(p_profile_node,p_sync_mode_node);

//设置profile的记录模式
	comment=BAD_CAST
    "\n\
    log_mode: STD,XML\n\
    STD print log on standard output or standard error\n\
    XML print log on log_path\n\
    ";
	p_comment_node=xmlNewComment(comment);
	xmlNodePtr p_log_mode_node=xmlNewNode(0,
					BAD_CAST"log_mode");
	xmlNodeSetContent(p_log_mode_node,
		BAD_CAST profile.get_log_mode_str().c_str());
	xmlAddChild(p_profile_node,p_comment_node);
	xmlAddChild(p_profile_node,p_log_mode_node);

//设置profile的日志记录路径
	comment=BAD_CAST
    "\n\
    log_path: the path to save log file when log_mode is XML\n\
    ";
	p_comment_node=xmlNewComment(comment);
	xmlNodePtr p_log_path_node=xmlNewNode(0,
					BAD_CAST"log_path");
	xmlNodeSetContent(p_log_path_node,
			BAD_CAST profile.get_log_path().c_str());
	xmlAddChild(p_profile_node,p_comment_node);
	xmlAddChild(p_profile_node,p_log_path_node);

//设置profile是否监控隐藏文件
	comment=BAD_CAST
    "\n\
    watch_hide: YES,NO\n\
    YES monitor the hidden file and directory\n\
    NO not monitor the hidden file and directory\n\
    ";
	p_comment_node=xmlNewComment(comment);
	xmlNodePtr p_watch_hide_node=xmlNewNode(0,BAD_CAST"watch_hide");
	if(profile.get_watch_hide())
	{
		xmlNodeSetContent(p_watch_hide_node,BAD_CAST"YES");
	}
	else
	{
		xmlNodeSetContent(p_watch_hide_node,BAD_CAST"NO");
	}
	xmlAddChild(p_profile_node,p_comment_node);
	xmlAddChild(p_profile_node,p_watch_hide_node);

//设置profile的源目录
	comment=BAD_CAST
    "\n\
    src_path: the src path will be monitored\n\
    ";
	p_comment_node=xmlNewComment(comment);
	xmlNodePtr p_src_path_node=xmlNewNode(0,BAD_CAST"src_path");
	xmlNodeSetContent(p_src_path_node,
			BAD_CAST profile.get_src_path().c_str());
	xmlAddChild(p_profile_node,p_comment_node);
	xmlAddChild(p_profile_node,p_src_path_node);

//设置profile的目标目录
	comment=BAD_CAST
    "\n\
    dest_path: the dest path will be synced or monitored\n\
    ";
	p_comment_node=xmlNewComment(comment);
	xmlNodePtr p_dest_path_node=xmlNewNode(0,BAD_CAST"dest_path");
	xmlNodeSetContent(p_dest_path_node,
			BAD_CAST profile.get_dest_path().c_str());
	xmlAddChild(p_profile_node,p_comment_node);
	xmlAddChild(p_profile_node,p_dest_path_node);

//设置profile的忽略监控目录
	comment=BAD_CAST
    "\n\
    ignore_path: the ignore path will NOT be monitored\n\
    ";
	p_comment_node=xmlNewComment(comment);
	xmlNodePtr p_ignore_path_node=xmlNewNode(0,BAD_CAST"ignore_path");

	vector<string> ignore_path_vec=profile.get_ignore_path_vec();
	for(size_t i=0;i<ignore_path_vec.size();i++)
	{
		xmlNodeSetContent(p_ignore_path_node,
				BAD_CAST ignore_path_vec[i].c_str());
		xmlAddChild(p_profile_node,p_comment_node);
		xmlAddChild(p_profile_node,p_ignore_path_node);
	}

//设置profile同步事件列表
	comment=BAD_CAST
    "\n\
    sync_event: CREATE_FILE,CREATE_DIR,DELETE,MODIFY,MOVED_FROM,MOVED_TO,MOVE_INSIDE\n\
    event marked in this label would be synchronized\n\
    ";
	p_comment_node=xmlNewComment(comment);
	xmlAddChild(p_profile_node,p_comment_node);

	xmlNodePtr p_sync_create_file=xmlNewNode(0,BAD_CAST"sync_event");
	if(profile.get_sync_create_file())
		xmlNodeSetContent(p_sync_create_file,
					BAD_CAST"CREATE_FILE");
	xmlAddChild(p_profile_node,p_sync_create_file);

	xmlNodePtr p_sync_create_dir=xmlNewNode(0,BAD_CAST"sync_event");
	if(profile.get_sync_create_dir())
		xmlNodeSetContent(p_sync_create_dir,BAD_CAST"CREATE_DIR");
	xmlAddChild(p_profile_node,p_sync_create_dir);

	xmlNodePtr p_sync_delete=xmlNewNode(0,BAD_CAST"sync_event");
	if(profile.get_sync_delete())
		xmlNodeSetContent(p_sync_delete,BAD_CAST"DELETE");
	xmlAddChild(p_profile_node,p_sync_delete);

	xmlNodePtr p_sync_modify=xmlNewNode(0,BAD_CAST"sync_event");
	if(profile.get_sync_modify())
		xmlNodeSetContent(p_sync_modify,BAD_CAST"MODIFY");
	xmlAddChild(p_profile_node,p_sync_modify);

	xmlNodePtr p_sync_moved_from=xmlNewNode(0,BAD_CAST"sync_event");
	if(profile.get_sync_moved_from())
		xmlNodeSetContent(p_sync_moved_from,BAD_CAST"MOVED_FROM");
	xmlAddChild(p_profile_node,p_sync_moved_from);

	xmlNodePtr p_sync_moved_to=xmlNewNode(0,BAD_CAST"sync_event");
	if(profile.get_sync_moved_to())
		xmlNodeSetContent(p_sync_moved_to,BAD_CAST"MOVED_TO");
	xmlAddChild(p_profile_node,p_sync_moved_to);

	xmlNodePtr p_sync_move_inside=xmlNewNode(0,BAD_CAST"sync_event");
	if(profile.get_sync_move_inside())
		xmlNodeSetContent(p_sync_move_inside,
					BAD_CAST"MOVE_INSIDE");
	xmlAddChild(p_profile_node,p_sync_move_inside);

//将profile的节点添加至root节点后
	xmlAddChild(p_root_node,p_profile_node);
//保存
	xmlSaveFormatFileEnc(profile_path.c_str(),p_doc,"UTF-8",1);
	return true;
}

bool
ProfileXml::has_profile(string profile_name)
{
	p_cur_node=p_root_node->xmlChildrenNode;
	//轮询每个根节点的子节点来查看是否存在profile
	while(0!=p_cur_node)
	{
		xmlChar *p_name;
		p_name=xmlGetProp(p_cur_node,BAD_CAST"name");
		if(!xmlStrcmp(p_name,BAD_CAST profile_name.c_str()))
			return true;
		p_cur_node=p_cur_node->next;
	}
	return false;
}

bool
ProfileXml::delete_profile(string profile_name)
{
	p_cur_node=p_root_node->xmlChildrenNode;
	while(0!=p_cur_node)
	{
		xmlChar *p_name;
		p_name=xmlGetProp(p_cur_node,BAD_CAST"name");
		if(!xmlStrcmp(p_name,BAD_CAST profile_name.c_str()))
		{
			xmlUnlinkNode(p_cur_node);
			xmlFreeNode(p_cur_node);
			xmlSaveFormatFileEnc(profile_path.c_str(),p_doc,"UTF-8",1);
			return true;
		}
		p_cur_node=p_cur_node->next;
	}
	//如果没有找到符合要求的profile就返回false
	return false;
}

void
ProfileXml::delete_all_profile()
{
	p_cur_node=p_root_node->xmlChildrenNode;
	while(0!=p_cur_node)
	{
		const xmlChar *node_name=p_cur_node->name;
		if(!xmlStrcmp(node_name,BAD_CAST"profile"))
		{
			xmlNodePtr tmp_node=p_cur_node->next;
			xmlUnlinkNode(p_cur_node);
			xmlFreeNode(p_cur_node);
			p_cur_node=tmp_node;
			xmlSaveFormatFileEnc(profile_path.c_str(),p_doc,"UTF-8",1);
		}
		else
			p_cur_node=p_cur_node->next;
	}
}

void
ProfileXml::get_profile_list(vector<string>& svec)
{
	p_cur_node=p_root_node->xmlChildrenNode;
	while(0!=p_cur_node)
	{
		xmlChar *p_name;
		p_name=xmlGetProp(p_cur_node,BAD_CAST"name");
		svec.push_back(string((const char*)p_name));
		p_cur_node=p_cur_node->next;
	}
}

bool
ProfileXml::get_profile(Profile& profile)
{
	p_cur_node=get_profile_node(profile.get_profile_name());
	//如果找不到对应节点则返回false
	if(NULL==p_cur_node)
		return false;
	//如果找到了则为profile设置各种详细信息
	p_cur_node=p_cur_node->xmlChildrenNode;
	while(0!=p_cur_node)
	{
		//查找sync_mode节点并赋值
		if(!xmlStrcmp(p_cur_node->name,BAD_CAST"sync_mode"))
		{
			xmlChar *content=xmlNodeGetContent(p_cur_node);
			if(!xmlStrcmp(content,BAD_CAST"RSYNC"))
				profile.set_sync_mode(RSYNC);
			if(!xmlStrcmp(content,BAD_CAST"ASYNC"))
				profile.set_sync_mode(ASYNC);
			if(!xmlStrcmp(content,BAD_CAST"NSYNC"))
				profile.set_sync_mode(NSYNC);
			p_cur_node=p_cur_node->next;
			continue;
		}
		//查找log_mode节点并赋值
		if(!xmlStrcmp(p_cur_node->name,BAD_CAST"log_mode"))
		{
			xmlChar *content=xmlNodeGetContent(p_cur_node);
			if(!xmlStrcmp(content,BAD_CAST"STD"))
				profile.set_log_mode(STD);
			if(!xmlStrcmp(content,BAD_CAST"XML"))
				profile.set_log_mode(XML);
			p_cur_node=p_cur_node->next;
			continue;
		}
		//查找log_path节点并赋值
		if(!xmlStrcmp(p_cur_node->name,BAD_CAST"log_path"))
		{
			xmlChar *content=xmlNodeGetContent(p_cur_node);
			profile.set_log_path((const char *)content);
			p_cur_node=p_cur_node->next;
			continue;
		}
		//查找watch_hide节点并赋值
		if(!xmlStrcmp(p_cur_node->name,BAD_CAST"watch_hide"))
		{
			xmlChar *content=xmlNodeGetContent(p_cur_node);
			profile.set_watch_hide(y_or_n2(content));
			p_cur_node=p_cur_node->next;
			continue;
		}
		//查找src_path节点并赋值
		if(!xmlStrcmp(p_cur_node->name,BAD_CAST"src_path"))
		{
			xmlChar *content=xmlNodeGetContent(p_cur_node);
			profile.set_src_path((const char *)content);
			p_cur_node=p_cur_node->next;
			continue;
		}
		//查找dest_path节点并赋值
		if(!xmlStrcmp(p_cur_node->name,BAD_CAST"dest_path"))
		{
			xmlChar *content=xmlNodeGetContent(p_cur_node);
			profile.set_dest_path((const char *)content);
			p_cur_node=p_cur_node->next;
			continue;
		}
		//查找ignore_path节点并赋值
		if(!xmlStrcmp(p_cur_node->name,BAD_CAST"ignore_path"))
		{
			xmlChar *content=xmlNodeGetContent(p_cur_node);
			profile.add_ignore_path((const char *)content);
			p_cur_node=p_cur_node->next;
			continue;
		}
		//查找sync_create_file节点并赋值
		if(!xmlStrcmp(p_cur_node->name,BAD_CAST"sync_event"))
		{
			xmlChar *content=xmlNodeGetContent(p_cur_node);
			if(!xmlStrcmp(content,BAD_CAST"CREATE_FILE"))
				profile.set_sync_create_file(true);
			if(!xmlStrcmp(content,BAD_CAST"CREATE_DIR"))
				profile.set_sync_create_dir(true);
			if(!xmlStrcmp(content,BAD_CAST"DELETE"))
				profile.set_sync_delete(true);
			if(!xmlStrcmp(content,BAD_CAST"MODIFY"))
				profile.set_sync_modify(true);
			if(!xmlStrcmp(content,BAD_CAST"MOVED_FROM"))
				profile.set_sync_moved_from(true);
			if(!xmlStrcmp(content,BAD_CAST"MOVED_TO"))
				profile.set_sync_moved_to(true);
			if(!xmlStrcmp(content,BAD_CAST"MOVE_INSIDE"))
				profile.set_sync_move_inside(true);
			p_cur_node=p_cur_node->next;
			continue;
		}
		p_cur_node=p_cur_node->next;
	}
	return true;
}

xmlNodePtr
ProfileXml::get_profile_node(string profile_name)
{
	xmlNodePtr p_tmp_node=NULL;
	p_tmp_node=p_root_node->xmlChildrenNode;
	while(NULL!=p_tmp_node)
	{
		//如果profile节点的name属性与要找的不相同则查找下一个
		if(xmlStrcmp(xmlGetProp(p_tmp_node,BAD_CAST"name"),
				BAD_CAST profile_name.c_str()))
		{
			p_tmp_node=p_tmp_node->next;
			continue;
		}
		return p_tmp_node;
	}
	//如果找不到同名的节点则返回NULL
	return NULL;
}

const char*
ProfileXml::y_or_n(bool val)
{
	switch(val)
	{
	case true:
		return "YES";
	case false:
		return "NO";
	}
}

bool
ProfileXml::y_or_n2(xmlChar *val)
{
	if(0==strcmp((const char*)val,"YES"))
		return true;
	if(0==strcmp((const char*)val,"NO"))
		return false;
	return 0;
}
